package compilator4urbi.compilator;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ResourceBundle;

import javax.xml.parsers.DocumentBuilderFactory;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import tom.library.emf.EcoreContainmentIntrospector;
import tom.library.sl.VisitFailure;
import behaviourdiagram.Behaviour;
import behaviourdiagram.Mission;
import behaviourdiagram.Sensor;
import compilator4urbi.Activator;
import compilator4urbi.util.BehaviourDiagramSerializer;

import java.util.List;
import java.util.ArrayList;
import java.util.Hashtable;

public class CompilatorWifibot{
     
    %include{missionMapping.tom} 
    %include { sl.tom }
    %include{dom_1_5.tom}
    %include { string.tom }


	%strategy generateUrbiCode() extends Fail() {
	    visit Sensor {
	      Sensor(name, _, _ ) -> { 	    	  
	         String sensorC = resourceBundle.getString("get.sensorContent");
             sensorC = sensorC.replace("{name}", `name);
             String sensorAngle = getSensorAngle(fileMapping, `name);
             sensorC = sensorC.replace("{angle}", sensorAngle);
	         sensorOutput.println(sensorC); 
	      }
	    }


	    visit Behaviour {
	       Behaviour (nameBehaviour,_,_,_,_,FSM(state, transition, initialState),_,_)-> {
              try{
                String pathName = path + File.separator + resourceBundle.getString("get.nameBehaviourFile").replace("{nameBehaviour}", `nameBehaviour); 
                FileWriter fileBehaviour = new FileWriter(pathName);
                PrintWriter behaviourOutput = new PrintWriter(fileBehaviour);
                loadFile += resourceBundle.getString("loadFile").replace("{path}", pathName).replace("\\", "\\\\") + "\n";
                behaviourOutput.println(resourceBundle.getString("get.behaviourHeader").replace("{nameBehaviour}", `nameBehaviour) + "\n"); 
                String initState = resourceBundle.getString("get.fsmStateContent");                
                initState = initState.replace("{nameBehaviour}", `nameBehaviour);
                initState = initState.replace("{nameState}", `initialState.getName());

                String code = `initialState.getCode();
                code = replaceString(code, nameSensorList);
                if ( `initialState
                                     instanceof fsm.FinalState)
                    {
                    	 String codeFinalState=resourceBundle.getString("get.codeFinalState");
                    	 codeFinalState = codeFinalState.replace("{nameBehaviour}",`nameBehaviour);
                    	 codeFinalState = codeFinalState.replace("{initialStateName}",`initialState.getName());
                    	 code += "\n" + codeFinalState + "\n"; 
                     }

                initState = initState.replace("{code}", code);
                initState = initState.replace("{bool}", "true");
                initState = initState.replace("{i0}", "1");
                behaviourOutput.println(initState + "\n"); 

               int j = 1;
                         
               %match(state){
                  StateEList(_*,etat, _*) -> {
                  if (!`etat.equals(`initialState)){
                	 
                	 String state = resourceBundle.getString("get.fsmStateContent");                
                     state = state.replace("{nameBehaviour}", `nameBehaviour);
                     state = state.replace("{nameState}", `etat.getName());

                     code = `etat.getCode();
                     code = replaceString(code, nameSensorList);
                     if ( `etat
                                     instanceof fsm.FinalState)
                     {
                    	 String codeFinalState=resourceBundle.getString("get.codeFinalState");
                    	 codeFinalState = codeFinalState.replace("{nameBehaviour}",`nameBehaviour);
                    	 codeFinalState = codeFinalState.replace("{initialStateName}",`initialState.getName());
                    	 code += "\n" + codeFinalState + "\n"; 
                     }

                	 state = state.replace("{code}", code);
                     state = state.replace("{bool}", "false");
                     state = state.replace("{i0}", ""+ ++j);
                     behaviourOutput.println(state + "\n"); 
                    
                    
                     }
                  }
                }     
               


                behaviourOutput.println(resourceBundle.getString("get.fsmTransitionComment"));
                int i = 1;
                %match(transition){
                  TransitionEList(_*, Transition(State(target,_,_,_,_), condition, State(src,_,_,_,_)), _*) -> {
                     String transition = resourceBundle.getString("get.fsmTransitionContent");
                     transition = transition.replace("{nameBehaviour}", `nameBehaviour);
                     transition = transition.replace("{source}", `src);
                     transition = transition.replace("{target}", `target);
                     String cond = replaceString(`condition, nameSensorList);
                     transition = transition.replace("{condition}", cond);
                     transition = transition.replace("{i0}", "" + i++);
                     behaviourOutput.println(transition);
                  }
                } 

				String endFile = resourceBundle.getString("get.behaviourEndFile");
				endFile = endFile.replace("{nameBehaviour}", `nameBehaviour);
                behaviourOutput.println("\n\n" + endFile);
 
                behaviourOutput.close();
              } catch (IOException e) {
	             e.printStackTrace();
              } 
           }
	    }
     }
   
    static String loadFile = "";
    static String path = "";
    static ResourceBundle resourceBundle = ResourceBundle.getBundle("compilator4urbi.conf.generateUrbiCodeWifibot");
    static FileWriter fileSensor = null; 
    static PrintWriter sensorOutput = null;
    static String fileMapping = null;
    static List<String> nameSensorList;
    static Hashtable<String, String> mapSensorType;       

    public static void  main(String[] args) throws VisitFailure { 
       File filename = new File("src/compilator4urbi/compilator/defaultWifibot.xmi");
       Mission mission = BehaviourDiagramSerializer.load(filename);
       generateUrbiCode(mission, filename);       
    }
   

    public static void init(String path){ 
       try{
           String pathName = path + File.separator + resourceBundle.getString("get.nameSensorFile");
           fileSensor = new FileWriter(pathName);
           sensorOutput = new PrintWriter(fileSensor);
           
           loadFile = resourceBundle.getString("loadFile").replace("{path}", pathName).replace("\\", "\\\\") + "\n";
 
        } catch (IOException e) {
	       e.printStackTrace();
        }
     }


    public static void generateArbiterCode(Mission mission, String path) {
	   %match (mission)  {
	     Mission (_ , behaviours , _)-> { try {
	         FileWriter file = new FileWriter(path + File.separator + resourceBundle.getString("get.nameArbitreFile")); 
             PrintWriter output = new PrintWriter(file);
            
             output.println(resourceBundle.getString("get.arbitreHeader") + "\n");
             output.println("var path = \""+path.replace("\\", "\\\\")+"\"");
             String fsm_u = resourceBundle.getString("loadFile").replace("{path}", path + File.separator + "fsm.u");
             fsm_u = fsm_u.replace("\\", "\\\\");
             output.println(fsm_u + "\n\n");
             output.println(loadFile);
            
             String fsm = "";
             String active = "";
             String freeze = "";
             String functionCond = "";
             String event="";
             String ats="";
           
             %match (behaviours) {
                BehaviourEList(_*, Behaviour (name,_,_,_,priorite,FSM(_, _, _),inputCond,outputCond), _*)-> 
                
               
                { 
                  String input_cond = replaceString(`inputCond, nameSensorList);
                  String output_cond = replaceString(`outputCond, nameSensorList);
                  fsm += "var fsm_"+`name+" = Tag.new|;\n";
                  active += "var active_fsm_"+`name+" = false|;\n";
                  event+="var Global.stop_fsm_"+`name+"_event = Event.new|;\n";
                  freeze += "fsm_"+`name+" :\n _root_"+`name+"_.enter(),\nfsm_"+`name+".freeze();\n\n";
                  functionCond += "function cond_"+`name+"()\n{\n" + input_cond+ "\n}|;\n\n";
                  functionCond += "function cond_quit_"+`name+"()\n{\n"  + output_cond+"\n}|;\n\n";
                  ats+="at (";
                 
                  for (Behaviour bh : `behaviours){
                	  if (`priorite< bh.getPriority() )
                		  ats+="not active_fsm_"+bh.getName()+" and not cond_"+bh.getName()+ " and ";  
                  }
                  ats+="not active_fsm_"+`name +" and ";
                  ats+="cond_"+`name +")\n{\n  ";
                  ats+="activeFSMChan << \"fsm_"+`name +"\" |\n";
                  for (Behaviour bh : `behaviours){
                	  if ( bh.getName().compareTo(`name)==0)
                		  ats+=" fsm_"+`name+ ".unfreeze() | active_fsm_"+`name+" = true |\n";
                	  else
                		  ats+=" fsm_"+bh.getName()+ ".freeze() | active_fsm_"+bh.getName()+" = false |\n";
                		    
                  }
                  ats+=";\n};\n\n";
                  ats+="at (";
                  ats+="cond_quit_"+`name + " and active_fsm_"+`name+")\n";
                  ats+="{\n";
                  ats+=" fsm_"+`name+".freeze() | active_fsm_"+`name+" = false;";
                  ats+="\n};\n\n";
                  ats+="at (";
                  ats+=" stop_fsm_"+`name + "_event?)\n";
                  ats+="{\n";
                  ats+="fsm_"+`name+".freeze() | active_fsm_"+`name+" = false;";
                  ats+="\n};\n\n";             
                }
             }       
             output.println(fsm + "\n" + active + "\n" +event +"\n"+"var activeFSMChan= Channel.new(\"activeFSM\")|;"+"\n\n"+ freeze +"\n" + functionCond+"\n"+ats);
             output.close();

	      	}
            catch (IOException e) {
	         e.printStackTrace();
          }

}}}


    public static void generateUrbiCode(Mission mission, File filename) throws VisitFailure{
	   
        fileMapping = filename + "map";
        path = filename.getParent();
        System.out.println("Genere les codes urbi pour wifibot !!!");
        System.out.println("***************************************************");
       
        init(path);
        nameSensorList = getListSensorName(mission);
       `TopDown(Try(generateUrbiCode())).visitLight(mission, new EcoreContainmentIntrospector());
        
        sensorOutput.close(); 
        
        generateArbiterCode (mission, path);
       
       
        copyFileFSM(path); 
        System.out.println("Tous les fichiers urbi pour wifibot ont ete generes avec succes"); 
        
    }


    
    static Document dom;

    /**
	m�thode faisant le mapping entre sensors abstraits et concrets
	
     **/
    public static String getSensorAngle(String filename, String nameSensor){
    	String angle = "";
    	try {
    		dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(filename);
    		Element elementRacine = dom.getDocumentElement();
    		angle = mapSensors(elementRacine,  nameSensor);
    	   	return angle;
        } catch (Exception e){
    		System.out.println("Fichier de mapping pour Wifibot introuvable: Sensor " + nameSensor);
    		return "";
    	}
    }

    private static String mapSensors(Node n,String name){
    	String angle = "";
    	%match (TNode n){
    		r@<_>s@<sensorDec Name=n1>s2@<sensorMap Type=t Angle=n2 />
    		</sensorDec></_>->{if (`n1.compareTo(name)==0){angle=`n2;}else {mapSensors(`s.getNextSibling(), name);}}
    	}
    	return angle;
    }

   private static String getTypeSensors(Node n, String name){
        if (n == null) return "";
    	String result = "";
    	%match (TNode n){
    		r@<_>s@<sensorDec Name=n1>s2@<sensorMap Type=t Angle=n2 />
    		</sensorDec></_>->{if (`n1.compareTo(name)==0){result=`t;}else {getTypeSensors(`s.getNextSibling(), name);}}
    	}
    	return result;
    }



    public static void copyFileFSM (String destPath)  {
    	try {
    		String dest=destPath+"/fsm.u";
    		File destFile=new File(dest); 
    	
    		BufferedReader in = Activator.getDefault().openFile("resources/fsm.u");
    		//BufferedReader in = new BufferedReader(new FileReader("resources/fsm.u"));
    		BufferedWriter out=new BufferedWriter(new FileWriter(destFile));
    		
    		char[] buf = new char[4096];
    		int n;
    		while ((n=in.read(buf, 0, buf.length)) > 0)
    		out.write(buf, 0, n);

    		in.close();
    		out.close();
    	}

    	catch (IOException e){
    		e.printStackTrace();
    	}
     }

      public static Element getElementRacineFileMapping(String filename){
    	try {
    		dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(filename);
    		Element elementRacine = dom.getDocumentElement();
    	   	return elementRacine;
        } catch (Exception e){
    		System.out.println("Fichier de mapping " + filename + " introuvable: ");
    		return null;
    	}
     }

     
      private static List<String> getListSensorName(Mission mission){
        List<String> result = new ArrayList<String>();
        mapSensorType = new Hashtable<String, String>();
        for(Sensor s : mission.getSensor()){
            result.add(s.getName());
            String typeSensor = getTypeSensors(getElementRacineFileMapping(fileMapping), s.getName());
            if(typeSensor.equals("speedAlpha"))
                mapSensorType.put(s.getName(), "wifibot.speedAlpha");
            else if(typeSensor.equals("speedDelta"))
                mapSensorType.put(s.getName(), "wifibot.speedDelta");
            else
                mapSensorType.put(s.getName(), "wifibot.{typeSensor}");
        }
        return result;
     }

     private static String replaceString(String chaine, List<String> nameList){
         String result = chaine;
         for(String s : nameList){
             result = result.replace(s, mapSensorType.get(s)); 
         }
         return result;
    }

}